// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-11-11

package common

import (
    cr "crypto/rand"
    eb "encoding/binary"
    "encoding/hex"
    mr "math/rand"
    "sync"
)

var singletonGenerator Generator

type (
    // Generator
    // is a tool use to generate trace and span identify.
    Generator interface {
        // Convert
        // converts a byte list to hex string
        Convert(bs []byte) string

        // Decode
        // hex string to byte list.
        Decode(str string) (b []byte)

        // Make
        // a byte list with given length
        //
        //   Generate.Make(8)
        //   Generate.Make(16)
        Make(n int) (b []byte)
    }

    generator struct {
        sync.Mutex
        data   int64
        err    error
        random *mr.Rand
    }
)

func NewGenerator() Generator {
    return singletonGenerator
}

func (o *generator) Convert(bs []byte) string {
    return hex.EncodeToString(bs)
}

func (o *generator) Decode(str string) (b []byte) {
    body := make([]byte, len(str)/2)
    if d, de := hex.DecodeString(str); de == nil {
        copy(body[:], d)
    }
    return body
}

func (o *generator) Make(n int) (b []byte) {
    b = make([]byte, n)
    o.Lock()
    defer o.Unlock()
    o.random.Read(b)
    return
}

// +---------------------------------------------------------------------------+
// | Access methods                                                            |
// +---------------------------------------------------------------------------+

func (o *generator) init() *generator {
    o.err = eb.Read(cr.Reader, eb.LittleEndian, &o.data)
    o.random = mr.New(mr.NewSource(o.data))
    return o
}
